#ifndef MATCHER_HPP
#define MATCHER_HPP
#include "util.hpp"
#include <list>
#include <mutex>
#include <thread>
#include <condition_variable>
#include"room.hpp"
#include"db.hpp"
#include"online.hpp"
template <class T>
class match_queue
{
private:
    std::list<T> _list; // 不使用queue是因为我们有删除数据的操作
    // 实现线程安全
    std::mutex _mutex;
    std::condition_variable _cond; // 条件变量,主要是为了阻塞消费者,后边的使用的时候,队列元素<2就阻塞
public:
    int size()
    {
        // 获取元素个数
        std::unique_lock<std::mutex> lock(_mutex);
        return _list.size();
    }
    // 判断是否为空
    bool empty()
    {
        std::unique_lock<std::mutex> lock(_mutex);
        return _list.empty();
    }
    // 阻塞队列
    void wait()
    {
        std::unique_lock<std::mutex> lock(_mutex);
        _cond.wait(lock);
    }
    // 入队数据,并唤醒线程
    void push(const T &data)
    {
        std::unique_lock<std::mutex> lock(_mutex);
        _list.push_back(data);
        // 唤醒一个线程
        _cond.notify_all();
    }
    // 出队数据
    bool pop(T &t)
    {
        std::unique_lock<std::mutex> lock(_mutex);
        if (_list.empty())
        {
            return false;
        }
        t = _list.front();
        _list.pop_front();
        return true;
    }
    void remove(const T &t)
    {
        std::unique_lock<std::mutex> lock(_mutex);
        _list.remove(t);
    }
};
class matcher
{
    // 1.有三个不同的匹配队列
    // 2.需要三个线程分别处理
    // 3.匹配成功之后,需要创建房间,所以需要房间管理的句柄
    // 4.在线用户管理模块的句柄
    // 5.数据管理模块操作句柄
    // 功能: 1.添加用户到匹配队列
    //      2.从匹配队列中删除用户
    //      3.线程入口函数
    //      4.匹配成功之后,创建房间
private:
    match_queue<uint64_t> _q_normal;
    match_queue<uint64_t> _q_advanced;
    match_queue<uint64_t> _q_expert;
    std::thread _normal_thread;
    std::thread _advanced_thread;
    std::thread _expert_thread;
    room_manager *_rm;
    user_table *_ut;
    online_manager *_om;

private:
    // 实现匹配的具体逻辑
    void handle_match(match_queue<uint64_t> &mq)
    {
        while (1)
        {
            // 1.判断队列的人数是否>2,<2则阻塞等待
            while (mq.size() < 2)
            {
                mq.wait();
            }

            // 2.此时代表人数够了,此时出队两个玩家
            uint64_t uid1, uid2;

            bool ret = mq.pop(uid1);
            if (ret == false)
            {
                continue;
            }
            ret = mq.pop(uid2);
            // 这里就是uid1出队成功了
            if (ret == false)
            {
                this->add(uid1);
                continue;
            }
            // 3.校验两个玩家是否是在线的,如果有人掉线,需要将另一人重新加入队列
            wsserver_t::connection_ptr conn1 = _om->get_conn_from_hall(uid1);
            if(conn1.get() == nullptr){
                this->add(uid2);
            }
            wsserver_t::connection_ptr conn2 = _om->get_conn_from_hall(uid2);
            if(conn2.get() == nullptr){
                this->add(uid1);
            }
            // 4.为两个玩家创建房间,并将玩家加入房间中
            room_ptr rp = _rm->create_room(uid1,uid2);
            if(rp.get() == nullptr){
                // 创建房间失败了,就需要将两个玩家加入队列重新匹配.
                this->add(uid1);
                this->add(uid2);
                continue;
            }
            // 5. 对两个玩家进行响应.
            Json::Value resp;
            resp["optype"] = "match_success";
            resp["result"] = true;
            std::string body;
            json_util::serialize(resp,body);
            conn1->send(body);
            conn2->send(body);
        }
    }
    void th_normal_entry()
    {
        return handle_match(_q_normal);
    }
    void th_advanced_entry()
    {
        return handle_match(_q_advanced);
    }
    void th_expert_entry()
    {
        return handle_match(_q_expert);
    }

public:
    matcher(room_manager *rm, user_table *ut, online_manager *om) : _rm(rm), _ut(ut), _om(om),
                                                                    _normal_thread(std::thread(&matcher::th_normal_entry, this)),
                                                                    _advanced_thread(std::thread(&matcher::th_advanced_entry, this)),
                                                                    _expert_thread(std::thread(&matcher::th_expert_entry, this))
    {
        DLOG("游戏匹配模块初始化成功!");
    }
    ~matcher()
    {
        _normal_thread.join();
        _advanced_thread.join();
        _expert_thread.join();
    }

    bool add(u_int64_t uid)
    {
        // 根据玩家的天梯分数,来判定玩家档次,添加到不同的匹配队列中
        // 1.根据用户id,获取玩家id
        Json::Value user;
        bool ret = _ut->select_by_id(uid, user);
        if (ret == false)
        {
            DLOG("获取玩家信息失败: %lu", uid);
            return false;
        }

        int score = user["score"].asInt();
        if (score < 2000)
        {
            _q_normal.push(uid);
        }
        else if (score >= 2000 && score < 3000)
        {
            _q_advanced.push(uid);
        }
        else
        {
            _q_expert.push(uid);
        }
        return true;
    }

    bool del(u_int64_t uid)
    {
        // 根据天梯分数信息,移除对应的队列
        Json::Value user;
        bool ret = _ut->select_by_id(uid, user);
        if (ret == false)
        {
            DLOG("获取玩家信息失败: %lu", uid);
            return false;
        }

        int score = user["score"].asInt();
        if (score < 2000)
        {
            _q_normal.remove(uid);
        }
        else if (score >= 2000 && score < 3000)
        {
            _q_advanced.remove(uid);
        }
        else
        {
            _q_expert.remove(uid);
        }
        return true;
    }
};
#endif